package me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.agv.inf.AgvMissionDao;
import me.zhengjie.minbearwcs.dao.stock.inf.PalletDao;
import me.zhengjie.minbearwcs.dao.storage.inf.LargeStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.MediumStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.StorageDao;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.Pallet;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.MediumStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.type.StorageType;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.ShiftingStorage5Or6FloorLocker;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class ShiftingStorage5Or6FloorLockerImpl implements ShiftingStorage5Or6FloorLocker {

    private final StorageDao storageDao;
    private final SmallStorageDao smallStorageDao;
    private final LargeStorageDao largeStorageDao;
    private final PalletDao palletDao;
    private final MediumStorageDao mediumStorageDao;
    private final AgvMissionDao agvmissidao;

    @Override
    public Tuple<String> lockInStorage(String materialCode, String inboundOrder, String batchCode, String palletCode, String oldMediumStorage) {
        // 1.先找物料号，单号，批次号相同且已经放了货物还有空位的中储位
        List<String> mediumStorageList = storageDao.selectCanUseInboundMediumExcludeNowStorage(
                        StorageType.STORAGE_AREA_5F,
                        StorageType.STORAGE_AREA_6F,
                        materialCode,
                        inboundOrder,
                        batchCode,
                        oldMediumStorage
                )
                .stream()
                .distinct()
                .collect(Collectors.toList());

        for (String mediumStorage : mediumStorageList) {
            SmallStorage smallStorage = findSmallStorageInMediumStorage(mediumStorage);
            if (smallStorage == null) continue;
            smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
            return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
        }

        // 2.找到同一订单的大储位
        List<String> largeStorageList = new ArrayList<>();
        largeStorageList = storageDao.selectSameInboundOrderLargeStorage(StorageType.STORAGE_AREA_5F, inboundOrder);
        if (largeStorageList.isEmpty()) {
            largeStorageList = storageDao.selectSameInboundOrderLargeStorage(StorageType.STORAGE_AREA_6F, inboundOrder);
        }
        if (!largeStorageList.isEmpty()) {
            for (String largeStorage : largeStorageList) {
                String mediumStorage = storageDao.selectEmptyMediumStorageByLargeStorage(largeStorage);
                SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(mediumStorage);
                if (smallStorage == null) continue;
                smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                mediumStorageDao.updateBatch1Info(mediumStorage, inboundOrder, materialCode, batchCode);
                return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
            }
        }

        // 3.找到有同一物料编号的楼层，按照大储位编号顺序，找到空的中储位，没有则找另外一层
        if (storageDao.selectHasMaterail(StorageType.STORAGE_AREA_5F, materialCode) > 0) {
            String mediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_5F);
            if (mediumStorage != null) {
                SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(mediumStorage);
                if (smallStorage != null) {
                    smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                    mediumStorageDao.updateBatch1Info(mediumStorage, inboundOrder, materialCode, batchCode);
                    return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
                }
            }
        }

        if (storageDao.selectHasMaterail(StorageType.STORAGE_AREA_6F, materialCode) > 0) {
            String mediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_6F);
            if (mediumStorage != null) {
                SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(mediumStorage);
                if (smallStorage != null) {
                    smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                    mediumStorageDao.updateBatch1Info(mediumStorage, inboundOrder, materialCode, batchCode);
                    return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
                }
            }
        }

        // 4.找一个空的中储位
        String randomMediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_5F);
        if (randomMediumStorage != null) {
            SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(randomMediumStorage);
            if (smallStorage != null) {
                smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                mediumStorageDao.updateBatch1Info(randomMediumStorage, inboundOrder, materialCode, batchCode);
                return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
            }
        }

        randomMediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_6F);
        if (randomMediumStorage != null) {
            SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(randomMediumStorage);
            if (smallStorage != null) {
                smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                mediumStorageDao.updateBatch1Info(randomMediumStorage, inboundOrder, materialCode, batchCode);
                return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
            }
        }


        // 5. 已经放了两个批次，有一个批次相同
        List<String> doubleBatchByBatch = storageDao.selectDoubleBatchByBatchExcludeNowStorage(
                        StorageType.STORAGE_AREA_5F,
                        StorageType.STORAGE_AREA_6F,
                        materialCode,
                        inboundOrder,
                        batchCode,
                        oldMediumStorage
                )
                .stream()
                .distinct()
                .collect(Collectors.toList());

        for (String mediumStorage : doubleBatchByBatch) {
            SmallStorage smallStorage = findHasDoubleBatchMediumStorage(mediumStorage, materialCode, inboundOrder, batchCode);
            if (smallStorage == null) continue;
            smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
            return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
        }


        // 5。已经放了一个批次
        List<String> OneBatchInboundMediumStorage = storageDao.selectHasOneBatchInboundMediumStorageExcludeNowStorage(
                        StorageType.STORAGE_AREA_5F,
                        StorageType.STORAGE_AREA_6F,
                        oldMediumStorage
                )
                .stream()
                .distinct()
                .collect(Collectors.toList());

        for (String mediumStorage : OneBatchInboundMediumStorage) {
            SmallStorage smallStorage = findHasOneBatchMediumStorage(mediumStorage, materialCode, inboundOrder, batchCode);
            if (smallStorage == null) continue;
            smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
            MediumStorage mediumTemp = mediumStorageDao.selectByMediumStorage(mediumStorage);
            if (mediumTemp.getBatchOne() == null) {
                mediumStorageDao.updateBatch1Info(
                        mediumStorage, inboundOrder, materialCode, batchCode
                );
            } else {
                mediumStorageDao.updateBatch2Info(
                        mediumStorage, inboundOrder, materialCode, batchCode
                );
            }
            return Tuple.tuple(smallStorage.getSmallStorageCode(), true);
        }

        return Tuple.tuple(null, false);
    }




    private SmallStorage findSmallStorageInMediumStorage(String mediumStorage) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        // 两口选最小
        if (enterCount == 2) {
            Optional<SmallStorage> min = smallStorageList.stream()
                    .filter(a -> a.getPalletCode() == null && a.getInboundFreezePallet() == null)
                    .min(Comparator.comparingInt(SmallStorage::getColumnNum));

            if (min.isPresent()) {
                return min.get();
            }
        }

        // 一口选对侧
        if (enterCount == 1) {
            SmallStorage smallStorage = smallStorageList.get(0);
            if (smallStorage.getIsEntrance() == 1) {
                for (int i = smallStorageList.size() - 1; i >= 0; i--) {
                    SmallStorage temp = smallStorageList.get(i);
                    if (temp.getPalletCode() == null && temp.getInboundFreezePallet() == null) {
                        return temp;
                    }
                }
            } else {
                for (int i = 0; i < smallStorageList.size(); i++) {
                    SmallStorage temp = smallStorageList.get(i);
                    if (temp.getPalletCode() == null && temp.getInboundFreezePallet() == null && temp.getOutboundFreezePallet() == null) {
                        return temp;
                    }
                }
            }
        }

        return null;
    }

    private SmallStorage findSmallStorageInEmptyMediumStorage(String mediumStorage) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        // 两口选最小
        if (enterCount == 2) {
            Optional<SmallStorage> optional = smallStorageList.stream()
                    .min(Comparator.comparingInt(SmallStorage::getColumnNum));

            if (optional.isPresent()) {
                return optional.get();
            }
        }

        // 一口选对侧
        if (enterCount == 1) {
            SmallStorage smallStorage = smallStorageList.get(0);
            if (smallStorage.getIsEntrance() == 1) {
                return smallStorageList.get(smallStorageList.size() - 1);
            } else {
                return smallStorage;
            }
        }

        return null;
    }

    private SmallStorage findHasDoubleBatchMediumStorage(String mediumStorage, String materialCode, String inboundOrder, String batchCode) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        if (enterCount < 2) return null;


        Map<String, Pallet> palletMap = new HashMap<>();

        for (SmallStorage smallStorage : smallStorageList) {
            String palletCode = smallStorage.getPalletCode();
            if (palletCode != null) {
                Pallet pallet = palletDao.selectByPalletCode(palletCode);
                palletMap.put(smallStorage.getSmallStorageCode(), pallet);
                continue;
            }
            String inboundFreezePallet = smallStorage.getInboundFreezePallet();
            if (inboundFreezePallet != null) {
                Pallet pallet = palletDao.selectByPalletCode(inboundFreezePallet);
                palletMap.put(smallStorage.getSmallStorageCode(), pallet);
                continue;
            }
        }


        Optional<SmallStorage> min = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .min(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> max = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .max(Comparator.comparingInt(SmallStorage::getColumnNum));


        if (min.isPresent() && max.isPresent()) {
            SmallStorage minStorage = min.get();
            Pallet minPallet = palletMap.get(minStorage.getSmallStorageCode());
            if (minPallet.getInboundOrderCode().equals(inboundOrder) &&
                    minPallet.getMaterialCode().equals(materialCode) &&
                    minPallet.getBatchCode().equals(batchCode)) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (min.get().getColumnNum() - 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                return optional.orElse(null);
            }


            SmallStorage maxStorage = max.get();
            Pallet maxPallet = palletMap.get(maxStorage.getSmallStorageCode());
            if (maxPallet.getInboundOrderCode().equals(inboundOrder) &&
                    maxPallet.getMaterialCode().equals(materialCode) &&
                    maxPallet.getBatchCode().equals(batchCode)) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (max.get().getColumnNum() + 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                return optional.orElse(null);
            }

        }


        return null;
    }


    private SmallStorage findHasOneBatchMediumStorage(String mediumStorage, String materialCode, String inboundOrder, String batchCode) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        if (enterCount < 2) return null;


        Optional<SmallStorage> min = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .min(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> max = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .max(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> minEnter = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .min(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> maxEnter = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .max(Comparator.comparingInt(SmallStorage::getColumnNum));


        if (min.isPresent() && max.isPresent() && minEnter.isPresent() && maxEnter.isPresent()) {
            int minCost = min.get().getColumnNum() - minEnter.get().getColumnNum();
            int maxCost = maxEnter.get().getColumnNum() - max.get().getColumnNum();

            if (minCost > maxCost) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (min.get().getColumnNum() - 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                if (optional.isPresent()) {
                    return optional.get();
                }
            }

            if (minCost <= maxCost) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (max.get().getColumnNum() + 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                if (optional.isPresent()) {
                    return optional.get();
                }
            }
        }


        return null;
    }


    @Override
    public Tuple<String> lockOutStorage(String smallStorage, String palletCode) {
        smallStorageDao.updateOutboundFreezeBySmallStorageCode(smallStorage, palletCode);
        return Tuple.tuple(null, true);
    }

}
